/**
@C++ Standard: 11
@Copyright:Copyright (c) 2019 Harold Hu. All rights reserved.
@Author: Created by Harold Hu with CLion on 2019/1/12 10:42.
@File Name: HaroldDebug.h
@Description: For elegant debug.
*/
#ifndef HAROLD_HAROLDDEBUG_H
#define HAROLD_HAROLDDEBUG_H
#ifdef __linux__
#define RESET   "\033[0m"
#define BLACK   "\033[30m"      /* Black */
#define RED     "\033[31m"      /* Red */
#define GREEN   "\033[32m"      /* Green */
#define YELLOW  "\033[33m"      /* Yellow */
#define BLUE    "\033[34m"      /* Blue */
#define MAGENTA "\033[35m"      /* Magenta */
#define CYAN    "\033[36m"      /* Cyan */
#define WHITE   "\033[37m"      /* White */
#define BOLDBLACK   "\033[1m\033[30m"      /* Bold Black */
#define BOLDRED     "\033[1m\033[31m"      /* Bold Red */
#define BOLDGREEN   "\033[1m\033[32m"      /* Bold Green */
#define BOLDYELLOW  "\033[1m\033[33m"      /* Bold Yellow */
#define BOLDBLUE    "\033[1m\033[34m"      /* Bold Blue */
#define BOLDMAGENTA "\033[1m\033[35m"      /* Bold Magenta */
#define BOLDCYAN    "\033[1m\033[36m"      /* Bold Cyan */
#define BOLDWHITE   "\033[1m\033[37m"      /* Bold White */
#define black(x) BLACK << (x) << RESET
#define red(x) RED << (x) << RESET
#define green(x) GREEN << (x) << RESET
#define yellow(x) YELLOW << (x) << RESET
#define blue(x) BLUE << (x) << RESET
#define magenta(x) MAGENTA << (x) << RESET
#define cyan(x) CYAN << (x) << RESET
#define white(x) WHITE << (x) << RESET
#define boldblack(x) BOLDBLACK << (x) << RESET
#define boldred(x) BOLDRED << (x) << RESET
#define boldgreen(x) BOLDGREEN << (x) << RESET
#define boldyellow(x) BOLDYELLOW << (x) << RESET
#define boldblue(x) BOLDBLUE << (x) << RESET
#define boldmagenta(x) BOLDMAGENTA << (x) << RESET
#define boldcyan(x) BOLDCYAN << (x) << RESET
#define boldwhite(x) BOLDWHITE << (x) << RESET
#else
#define RESET
#define BLACK
#define RED
#define GREEN
#define YELLOW
#define BLUE
#define MAGENTA
#define CYAN
#define WHITE
#define BOLDBLACK
#define BOLDRED
#define BOLDGREEN
#define BOLDYELLOW
#define BOLDBLUE
#define BOLDMAGENTA
#define BOLDCYAN
#define BOLDWHITE
#define black(x) x
#define red(x) x
#define green(x) x
#define yellow(x) x
#define blue(x) x
#define magenta(x) x
#define cyan(x) x
#define white(x) x
#define boldblack(x) x
#define boldred(x) x
#define boldgreen(x) x
#define boldyellow(x) x
#define boldblue(x) x
#define boldmagenta(x) x
#define boldcyan(x) x
#define boldwhite(x) x
#endif

#define TEST_NO 0///0: Do No test;
#define TEST_GTEST 1///1: Do test with googleTest;
#define TEST_DOC 2///2: Do test with docTest;
#define TEST_BOOST 3///3: Do test with boostTest;
#define TEST_BENCHMARK 4///4: Do test with Benchmark;

#define TEST_TYPE TEST_BENCHMARK

#include <iostream>
#include <cassert>
#include <string>
#include <boost/timer.hpp>
#include <boost/type_index.hpp>

using std::cout;
using std::endl;
using std::string;
using boost::typeindex::type_id_with_cvr;

struct debugEnterOut
{
	string funcName_;
	boost::timer time;
	
	explicit debugEnterOut(const string& funcName) : funcName_(funcName)
	{
		cout << boldgreen("\nEnter Function: ") << magenta(funcName) << endl << endl;
	}
	
	~debugEnterOut()
	{
		cout << boldgreen("\nFunction (<== ") << magenta(funcName_) << boldgreen(" ==>) takes: ")
			 << magenta(time.elapsed()) << boldgreen("  seconds") << endl;
		cout << boldgreen("\nLeave Function: ") << magenta(funcName_) << endl << endl;
	}
};

struct HuHaiboTime
{
	boost::timer time;
	string message;
	
	explicit HuHaiboTime(const string& timeKeepOperation) : message(timeKeepOperation)
	{
		cout << boldgreen("\nTIME KEEP START! -- ") << magenta(timeKeepOperation) << endl << endl;
	}
	
	~HuHaiboTime()
	{
		cout << boldblue("\nOperation (<==\" ") << magenta(message) << boldblue(" \"==>) takes: ")
			 << magenta(time.elapsed()) << boldblue("  seconds") << endl << endl;
		cout << boldgreen("\nTIME KEEP END! -- ") << magenta(message) << endl;
	}
};

#ifdef __GNUC__

#include <cxxabi.h>

#endif

#define CONNECTION(text1, text2) (text1##text2)

#define __DEBUG_ON__ true
#if __DEBUG_ON__
#define DEBUG_ENTER_OUT debugEnterOut DEO(__func__);
#define START_KEEP_TIME(x) HuHaiboTime* CONNECTION(x,27) = new HuHaiboTime(x);
#define END_KEEP_TIME(x) if (nullptr != CONNECTION(x,27)) { delete CONNECTION(x,27); CONNECTION(x,27) = nullptr; }

#ifdef __GNUC__
/* #define DEBUG_SHOW(x) */
#define DEBUG_SHOW(x) cout << boldgreen("\n=== DEBUG_SHOW BEGIN ===\n") << blue(">>> Position:\n--> ") << magenta(__FILE__) << blue("\tline: ") << magenta(__LINE__) << "\n"; \
    cout << blue(">>> Element:\n--> ") << magenta(#x) << blue(" (type: ") << magenta(abi::__cxa_demangle(typeid(x).name(), 0, 0, 0)) << blue(") = ") << magenta(x)  << boldgreen("\n=== DEBUG_SHOW END ===\n");
/* #define DEBUG_TYPE(x) */
#define DEBUG_TYPE(x) cout << boldgreen("\n=== DEBUG_TYPE BEGIN ===\n") << blue(">>> Position:\n--> ") << magenta(__FILE__) << blue("\tline: ") << magenta(__LINE__) << "\n"; \
    cout << blue(">>> Element:\n--> ") << magenta(#x) << blue(" (type: ") << magenta(abi::__cxa_demangle(typeid(x).name(), 0, 0, 0)) << blue(")")  << boldgreen("\n=== DEBUG_TYPE END ===\n");
#define PRINT_TYPE(x) cout << #x << " (type: " << type_id_with_cvr<decltype(x)>().pretty_name() << ")" << endl;
#define PRINT_T_TYPE(x) cout << #x << " (type: " << type_id_with_cvr<x>().pretty_name() << ")" << endl;
#else
#define DEBUG_SHOW(x) cout << "\n=== DEBUG_SHOW BEGIN ===\n>>> Position:\n--> " << __FILE__ << "\tline: " << __LINE__ << "\n>>> Element:\n--> "; \
	cout << #x << " (type: " << typeid(x).name() << ")" << " = " << (x) << "\n=== DEBUG_SHOW END ===\n";
#define DEBUG_TYPE(x) cout << "\n=== DEBUG_TYPE BEGIN ===\n>>> Position:\n--> " << __FILE__ << "\tline: " << __LINE__ << "\n>>> Element:\n--> "; \
	cout << #x << " (type: " << typeid(x).name() << ")"  << "\n=== DEBUG_TYPE END ===\n";
#define PRINT_TYPE(x) cout << #x << " (type: " << type_id_with_cvr<decltype(x)>().pretty_name() << ")" << endl;
#define PRINT_T_TYPE(x) cout << #x << " (type: " << type_id_with_cvr<x>().pretty_name() << ")" << endl;
#endif

/* #define DEBUG_FUNC */
#define DEBUG_FUNC cout << boldgreen("\n=== DEBUG_FUNC BEGIN ===\n") << blue(">>> Position:\n--> ") << magenta(__FILE__) << blue("\tline: ") << magenta(__LINE__) << "\n"; \
    cout << blue(">>> Run into function:\n--> ") << magenta(__func__) << blue("(...)") <<  boldgreen("\n=== DEBUG_FUNC END ===\n");

/* #define DEBUG */
#ifdef __GNUC__
#define DEBUG cout << boldgreen("\n=== DEBUG BEGIN ===\n") << blue(">>> Position:\n--> ") << magenta(__FILE__) << blue("\tline: ") << magenta(__LINE__) << "\n"; \
    cout << blue(">>> Compiler && version:\n--> GCC: ") << magenta(__GNUC__) << magenta(".") << magenta(__GNUC_MINOR__) << magenta(".") << magenta(__GNUC_PATCHLEVEL__) << blue("\n>>> Compile time:\n--> ") << magenta(__DATE__) << "\t" << magenta(__TIME__) << boldgreen("\n=== DEBUG END ===\n");
#endif
#ifdef _MSC_VER
#define DEBUG cout << "\n=== DEBUG BEGIN ===\n>>> Position:\n--> " << __FILE__ << "\tline: " << __LINE__ << "\n"; \
	cout << ">>> Compiler && version:\n--> Microsoft C Compiler: " << _MSC_FULL_VER << "\n>>> Compile time:\n--> " << __DATE__ << "\t" << __TIME__  << "\n=== DEBUG END ===\n";
#endif

#else
#define DEBUG_ENTER_OUT
#define START_KEEP_TIME(x)
#define END_KEEP_TIME
#define DEBUG_SHOW(x)
#define DEBUG_TYPE(x)
#define DEBUG_FUNC
#define DEBUG
#define PRINT_TYPE(x)
#define PRINT_TYPE(x)
#define PRINT_T_TYPE(x)
#endif
#endif //HAROLD_HAROLDDEBUG_H
